﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ServiceModel.Channels;
using System.ServiceModel;
using System.Text;

namespace USTF.Media.MediaServiceConsumer
{

    /// <summary>
    /// Custom binding element - builds the inner channel
    /// </summary>
    public class ClientChannelBindingElement : BindingElement
    {
        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        public ClientChannelBindingElement()
        {
        }
        /// <summary>
        /// Protected constructor
        /// </summary>
        /// <param name="other">Another binding element</param>
        protected ClientChannelBindingElement(ClientChannelBindingElement other)
            : base(other)
        {
        }
        #endregion
        #region Public methods
        /// <summary>
        /// Build a channel factory
        /// </summary>
        /// <typeparam name="TChannel">Channel of interest</typeparam>
        /// <param name="context">Binding context</param>
        /// <returns>A new channel factory</returns>
        public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
        {
            ClientChannelFactory<TChannel> factory = new ClientChannelFactory<TChannel>();
            factory.InnerChannelFactory = context.BuildInnerChannelFactory<TChannel>();
            return factory;
        }
        /// <summary>
        /// Copy this object
        /// </summary>
        /// <returns>A copy of this</returns>
        public override BindingElement Clone()
        {
            return new ClientChannelBindingElement(this);
        }
        /// <summary>
        /// Gets an element property
        /// </summary>
        /// <typeparam name="T">Channel of interest</typeparam>
        /// <param name="context">Binding context</param>
        /// <returns>Property value</returns>
        public override T GetProperty<T>(BindingContext context)
        {
            return context.GetInnerProperty<T>();
        }
        #endregion
    }


    /// <summary>
    /// Custom Silverlight binding (mimics BasicHttpBinding)
    /// </summary>
    public class ClientChannelBinding : CustomBinding
    {
        public string Username { get; set; }
        public string Password { get; set; }

        #region Constructor
        /// <summary>
        /// Default constructor
        /// </summary>
        public ClientChannelBinding()
        {
            BasicHttpBinding basic = new BasicHttpBinding();
            basic.MaxBufferSize = 2147483647;
            basic.MaxReceivedMessageSize = 2147483647;
            this.Elements.Add(new ClientChannelBindingElement());
            this.Elements.Add(new TextMessageEncodingBindingElement(basic.MessageVersion, basic.TextEncoding));
            this.Elements.Add(new HttpTransportBindingElement() { MaxBufferSize = 2147483647, MaxReceivedMessageSize = 2147483647 });
            
        }
        #endregion
        #region Public methods
        /// <summary>
        /// Builds the channel factory
        /// </summary>
        /// <typeparam name="TChannel">Channel of interest</typeparam>
        /// <param name="parameters">Binding parameters</param>
        /// <returns>An encapsulated channel factory</returns>
        public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingParameterCollection parameters)
        {
            ClientChannelFactory<TChannel> factory = new ClientChannelFactory<TChannel>() { Username = Username, Password = Password };
            BasicHttpBinding binding = new BasicHttpBinding();

            binding.MaxBufferSize = 2147483647;
            binding.MaxReceivedMessageSize = 2147483647;

            factory.InnerChannelFactory = binding.BuildChannelFactory<TChannel>();
            return factory;
        }
        #endregion
        #region Internal methods
        /// <summary>
        /// Async Opened event handler
        /// </summary>
        /// <typeparam name="TChannel">Channel of interest</typeparam>
        /// <param name="result">Async result</param>
        internal void OnOpened<TChannel>(IAsyncResult result)
        {
            if (result.CompletedSynchronously)
            {
                return;
            }
            else
            {
                CompleteOpen<TChannel>(result);
            }
        }
        /// <summary>
        /// Async callback handler
        /// </summary>
        /// <typeparam name="TChannel">Channel of interest</typeparam>
        /// <param name="result">Async result</param>
        internal void CompleteOpen<TChannel>(IAsyncResult result)
        {
            IChannelFactory<TChannel> factory = (IChannelFactory<TChannel>)result.AsyncState;
            factory.EndOpen(result);
        }
        #endregion
    }



    /// <summary>
    /// Silverlight client channel class
    /// </summary>
    public class ClientChannel : IRequestChannel
    {
        #region Fields
        /// <summary>
        /// Internal channel
        /// </summary>
        private IRequestChannel innerChannel;
        #endregion
        #region Constructors
        /// <summary>
        /// Channel Constructor
        /// </summary>
        /// <param name="innerChannel">Inner channel</param>
        public ClientChannel(IRequestChannel innerChannel)
        {
            this.innerChannel = innerChannel;
        }
        #endregion
        #region Events
        /// <summary>
        /// Close event handler
        /// </summary>
        public event EventHandler Closed;
        /// <summary>
        /// Closing event handler
        /// </summary>
        public event EventHandler Closing;
        /// <summary>
        /// Faulted event handler
        /// </summary>
        public event EventHandler Faulted;
        /// <summary>
        /// Open event handler
        /// </summary>
        public event EventHandler Opened;
        /// <summary>
        /// Opening event handler
        /// </summary>
        public event EventHandler Opening;
        #endregion
        #region Properties
        /// <summary>
        /// Gets the remote address of the channel
        /// </summary>
        public EndpointAddress RemoteAddress
        {
            get
            {
                return this.innerChannel.RemoteAddress;
            }
        }
        /// <summary>
        /// Gets the coomunication state object
        /// </summary>
        public CommunicationState State
        {
            get
            {
                return this.innerChannel.State;
            }
        }
        /// <summary>
        /// Gets the URI (from inner channel)
        /// </summary>
        public Uri Via
        {
            get
            {
                return this.innerChannel.Via;
            }
        }
        #endregion
        #region Methods
        /// <summary>
        /// Async begin request
        /// </summary>
        /// <param name="message">Message to send</param>
        /// <param name="timeout">Timeout period</param>
        /// <param name="callback">Callback method</param>
        /// <param name="state">State information</param>
        /// <returns>Asynchronous result</returns>
        public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
        {
            // In Silverlight this is the place to add the header to the outgoing request 
            string credentials = "Basicc" + TemporaryCredentialStore.Current.Username + ":" + TemporaryCredentialStore.Current.Password;
            //Encoding encoding = Encoding.GetEncoding("UTF-8");
            //string userPass = Convert.ToBase64String(encoding.GetBytes(credentials));
            
            message.Headers.Add(MessageHeader.CreateHeader("SUMOAuth", "SUMO", credentials));
            IAsyncResult result = this.innerChannel.BeginRequest(message, timeout, callback, state);
            return result;
        }

        public string Username { get; set; }
        public string Password { get; set; }

        /// <summary>
        /// Async begin request
        /// </summary>
        /// <param name="message">Message to send</param>
        /// <param name="callback">Callback method</param>
        /// <param name="state">State information</param>
        /// <returns>Asynchronous result</returns>
        public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state)
        {
            string credentials = "Basicc" + TemporaryCredentialStore.Current.Username + ":" + TemporaryCredentialStore.Current.Password;
            //Encoding encoding = Encoding.GetEncoding("UTF-8");
            //string userPass = Convert.ToBase64String(encoding.GetBytes(credentials));

            message.Headers.Add(MessageHeader.CreateHeader("SUMOAuth", "SUMO", credentials));
            //this.Decorate(message);
            IAsyncResult result = this.innerChannel.BeginRequest(message, callback, state);
            return result;
        }
        /// <summary>
        /// End the request - parse the reply for the security header
        /// </summary>
        /// <param name="result">Async results object</param>
        /// <returns><c>Message</c> reply</returns>
        public Message EndRequest(IAsyncResult result)
        {
            // In Silverlight this is the place to read the header from the incoming reply
            Message reply = this.innerChannel.EndRequest(result);
            //... READ THE HEADER HERE ...

            bool authenticated = true;
            foreach (var item in reply.Headers)
            {
                if (item.Name == "WWW-Authenticate")
                    authenticated = false;
            }
            if (!authenticated)
                MessageBox.Show("You did not have permission to complete the operation.");

            return reply;
        }
        /// <summary>
        /// Synchronous request
        /// </summary>
        /// <param name="message">Outgoing message</param>
        /// <param name="timeout">Timeout period</param>
        /// <returns>Modified message</returns>
        public Message Request(Message message, TimeSpan timeout)
        {
            //this.Decorate(message);
            return this.innerChannel.Request(message, timeout);
        }
        /// <summary>
        /// Synchronous request
        /// </summary>
        /// <param name="message">Outgoing message</param>
        /// <returns>Modified message</returns>
        public Message Request(Message message)
        {
            //this.Decorate(message);
            return this.innerChannel.Request(message);
        }
        /// <summary>
        /// Gets a channel property
        /// </summary>
        /// <typeparam name="T">Type of property</typeparam>
        /// <returns>Property result</returns>
        public T GetProperty<T>() where T : class
        {
            return this.innerChannel.GetProperty<T>();
        }
        /// <summary>
        /// Abort this channel
        /// </summary>
        public void Abort()
        {
            this.innerChannel.Abort();
        }
        /// <summary>
        /// Asynchronous close
        /// </summary>
        /// <param name="timeout">Timeout period</param>
        /// <param name="callback">Callback method</param>
        /// <param name="state">State information</param>
        /// <returns>Asynchronous result</returns>
        public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return this.innerChannel.BeginClose(timeout, callback, state);
        }
        /// <summary>
        /// Asynchronous close
        /// </summary>
        /// <param name="callback">Callback method</param>
        /// <param name="state">State information</param>
        /// <returns>Asynchronous result</returns>
        public IAsyncResult BeginClose(AsyncCallback callback, object state)
        {
            return this.innerChannel.BeginClose(callback, state);
        }
        /// <summary>
        /// Asynchronous open
        /// </summary>
        /// <param name="timeout">Timeout period</param>
        /// <param name="callback">Callback method</param>
        /// <param name="state">State information</param>
        /// <returns>Asynchronous result</returns>
        public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return this.innerChannel.BeginOpen(timeout, callback, state);
        }
        /// <summary>
        /// Asynchronous open
        /// </summary>
        /// <param name="callback">Callback method</param>
        /// <param name="state">State information</param>
        /// <returns>Asynchronous result</returns>
        public IAsyncResult BeginOpen(AsyncCallback callback, object state)
        {
            return this.innerChannel.BeginOpen(callback, state);
        }
        /// <summary>
        /// Synchronous close
        /// </summary>
        /// <param name="timeout">Timeout period</param>
        public void Close(TimeSpan timeout)
        {
            this.innerChannel.Close(timeout);
        }
        /// <summary>
        /// Synchronous close
        /// </summary>
        public void Close()
        {
            this.innerChannel.Close();
        }
        /// <summary>
        /// Asynchronous callback - close
        /// </summary>
        /// <param name="result">Async result</param>
        public void EndClose(IAsyncResult result)
        {
            this.innerChannel.EndClose(result);
        }
        /// <summary>
        /// Asynchronous callback - open
        /// </summary>
        /// <param name="result">Async result</param>
        public void EndOpen(IAsyncResult result)
        {
            this.innerChannel.EndOpen(result);
        }
        /// <summary>
        /// Synchronous open
        /// </summary>
        /// <param name="timeout">Timeout period</param>
        public void Open(TimeSpan timeout)
        {
            this.innerChannel.Open(timeout);
        }
        /// <summary>
        /// Synchronous open
        /// </summary>
        public void Open()
        {
            this.innerChannel.Open();
        }
        #endregion
        #region Protected Methods
     
        #endregion
    }

    //[Note: the SecurityHeader class is derived from the MessageHeader class]

    //... and an associated channel factory:


    /// <summary>
    /// Overriden custom channel factory
    /// </summary>
    /// <typeparam name="TChannel">Type of channel to create</typeparam>
    public class ClientChannelFactory<TChannel> : ChannelFactoryBase<TChannel>
    {
        public string Username { get; set; }
        public string Password { get; set; }

        #region Fields
        /// <summary>
        /// Inner channel factory
        /// </summary>
        private IChannelFactory<TChannel> innerChannelFactory;
        #endregion
        #region Constructor
        /// <summary>
        /// Default constructor
        /// </summary>
        public ClientChannelFactory()
        {
        }
        #endregion
        #region Properties
        /// <summary>
        /// Gets or sets the inner channel factory
        /// </summary>
        public IChannelFactory<TChannel> InnerChannelFactory
        {
            get
            {
                return this.innerChannelFactory;
            }
            set
            {
                this.innerChannelFactory = value;
            }
        }
        #endregion
        #region Protected methods
        /// <summary>
        /// Open event handler
        /// </summary>
        /// <param name="timeout">Timeout period</param>
        protected override void OnOpen(TimeSpan timeout)
        {
            this.innerChannelFactory.Open(timeout);
        }
        /// <summary>
        /// Create channel evebt handler
        /// </summary>
        /// <param name="to">Destination of channel</param>
        /// <param name="via">Via this URI</param>
        /// <returns>Newly created channel</returns>
        protected override TChannel OnCreateChannel(EndpointAddress to, Uri via)
        {
            

            TChannel innerchannel = this.innerChannelFactory.CreateChannel(to, via);
            if (innerchannel is IRequestChannel)
            {
                ClientChannel clientChannel = new ClientChannel((IRequestChannel)innerchannel) { Username = Username, Password = Password };
                
                return (TChannel)(object)clientChannel;
            }
            return innerchannel;
        }
        /// <summary>
        /// End open async callback handler
        /// </summary>
        /// <param name="result">Async result</param>
        protected override void OnEndOpen(IAsyncResult result)
        {
            this.innerChannelFactory.EndOpen(result);
        }
        /// <summary>
        /// Begin open async handler
        /// </summary>
        /// <param name="span">Timeout span</param>
        /// <param name="callback">Callback method</param>
        /// <param name="asyncState">Async state object</param>
        /// <returns>Async result</returns>
        protected override IAsyncResult OnBeginOpen(TimeSpan span, AsyncCallback callback, object asyncState)
        {
            return this.innerChannelFactory.BeginOpen(callback, asyncState);
        }
        #endregion
    }



}
